ひとりNavigation API Advent Calendar 19日目
https://gyazo.com/8231fffebf08cecf8c8eb1aa137892d7
これはひとりNavigation API Advent Calendarの19日目です。
今日はAstroのView Transitions機能を見ていきます
<ClientRouter />を呼び出すことでSPAのような挙動になる
View transitions | Docs
Astro 3.0 | Astro
当初は<ViewTransitions />を呼び出す形だったのが<ClientRouter />に差し変わった
https://github.com/withastro/astro/blob/ebc4b1cde82c76076d5d673b5b70f94be2c066f3/packages/astro/src/transitions/router.ts
ルーティング処理を扱う部分
クリックイベントのインターセプト処理
https://github.com/withastro/astro/blob/ebc4b1cd/packages/astro/components/ClientRouter.astro#L67-L108
Sveltekitと比べるとあっさりしてるyamanoku.icon
仕組み
https://docs.astro.build/ja/guides/view-transitions/#client-side-navigation-process
クライアントサイドナビゲーションのプロセス
1. サイト訪問者が以下のいずれかの操作でナビゲーションを開始
サイト内の別ページへリンクする <a> タグをクリック
ブラウザの「戻る」ボタンをクリック
ブラウザの「進む」ボタンをクリック
2. ルーターが次のページの取得を開始
3. ルーターは <html> 要素に data-astro-transition 属性を追加し、値を "forward" または "back" に設定
4. ルーターは document.startViewTransition を呼び出す
これによりブラウザ独自のView Transitionのプロセスが開始
ブラウザは現在のページ状態をスクリーンショットする
5. startViewTransition のコールバック内で、ルーターは swap(入れ替え) を実行する
<head> の内容を入れ替え。
ただし以下は保持される
新しいページにも存在するスタイルシート DOM ノード(FOUC防止のため)
新しいページにも存在するスクリプト
transition:persist が付与され、新しいページにも対応要素がある <head> 要素
<body> を新しいページの <body> に完全に置き換え
transition:persist が付与された要素は、新しいページに存在する場合、新しい DOM に移動
必要に応じてスクロール位置を復元する
astro:after-swap イベントを document に発火(swap処理の終了)
6. ルーターは新しいスタイルシートの読み込みを待機し、TransitionをResolveする
7. ルーターは新しいページに追加されたスクリプトを実行
8. astro:page-load イベントが発火
これでナビゲーションプロセスの終了
code:js
export async function navigate(href: string, options?: Options) {
if (inBrowser === false) {
if (!navigateOnServerWarned) {
// instantiate an error for the stacktrace to show to user.
const warning = new Error(
'The view transitions client API was called during a server side render. This may be unintentional as the navigate() function is expected to be called in response to user interactions. Please make sure that your usage is correct.',
);
warning.name = 'Warning';
console.warn(warning);
navigateOnServerWarned = true;
}
return;
}
await transition('forward', originalLocation, new URL(href, location.href), options ?? {});
}
transtion()内での処理
コンテンツのフェッチ
doPreparation()イベントが発火され、新しいドキュメントがパースされ、スタイルシートのプリロードなどの準備処理がされる
ブラウザがView Transitions APIをサポートしている場合、document.startViewTransition()を使用してスムーズなアニメーション付きの遷移が実行される
doSwap()イベントが発火され、swap()関数によって現在のページのDOM(head要素とbody要素)が新しいページのDOMに置き換えられる
swap()関数は以下の処理を順次実行する
スクリプトの実行状態のマーク
ルート要素(<html>)の属性の更新
<head>の更新
フォーカス状態の保存
<body>の置き換え
フォーカス状態の復元
document.activeElementされるものを探してフォーカスさせる処理
saveFocus()
restoreFocus()
moveToLocation()関数により、History APIを使ってブラウザの履歴が更新され、適切なスクロール位置が設定
DOMの更新後、runScripts()関数により新しいページのスクリプトが適切に実行
Route Announcer相当の関数もある
code:js
const announce = () => {
let div = document.createElement('div');
div.setAttribute('aria-live', 'assertive');
div.setAttribute('aria-atomic', 'true');
div.className = 'astro-route-announcer';
document.body.append(div);
setTimeout(
() => {
let title = document.title || document.querySelector('h1')?.textContent || location.pathname;
div.textContent = title;
},
// Much thought went into this magic number; the gist is that screen readers
// need to see that the element changed and might not do so if it happens
// too quickly.
60,
);
};
60の根拠があいまいなのがね…yamanoku.icon
code:js
currentTransition.viewTransition?.updateCallbackDone.finally(async () => {
await runScripts();
onPageLoad();
announce();
});
スクリプト実行、ページロードののちにRoute Announcerが起動
参考:View TransitionでわかるNext.jsとAstroの設計思想